#include "SimulationParameters.h"
#include <sstream>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <ctime>
#include "ComponentsHolder.h"
using namespace std;
int SimulationParameters::CacheAlignment;
double SimulationParameters::SimulationTime;
int SimulationParameters::CacheSize;
int SimulationParameters::FlashSize;
std::string SimulationParameters::CachePolicy;
int SimulationParameters::CacheReadPart;
int SimulationParameters::SATACount;
int SimulationParameters::FCCount;
int SimulationParameters::SSDCount;
double SimulationParameters::WriteAllowance;
bool SimulationParameters::UseCache;
int SimulationParameters::BringNextExtents;
int SimulationParameters::SequentialTimeDiff;
bool SimulationParameters::ConsiderHits;
bool SimulationParameters::UsePriority;
string SimulationParameters::AllFiles;
vector<double> SimulationParameters::group_percents;
string SimulationParameters::algo;
int SimulationParameters::exchange_limit;
vector<double> SimulationParameters::desiredRT;
int SimulationParameters::placementDistribution;

vector<string> SimulationParameters::dataFiles;
string SimulationParameters::IOFileName;

string SimulationParameters::PriorityFile;
string SimulationParameters::AggregatedFile;
string SimulationParameters::OutputDirectory;
int SimulationParameters::NumberOfRounds; // number of times to run the data
bool SimulationParameters::StaticPlacements;

int SimulationParameters::SSDSize;
int SimulationParameters::FCSize;
int SimulationParameters::SATASize;

//bool SimulationParameters::insertToBothCaches;
FlashPolicy SimulationParameters::flashPolicy;
using namespace std;
void test();
void SimulationParameters::init() 
{
	
	
	srand(time(NULL));
	
	desiredRT = std::vector<double>(3);
	group_percents = std::vector<double>(3);

	//PriorityFile = "E:/InputFiles/LunsPriorityGroups/";
	//AggregatedFile = "E:/outputFilesBunched/Aggregated";

	readParameters();
	updateDate();
	//AggregatedFile = OutputDirectory+"Aggregated.txt";
	createDirectory("E:/Output");
	//createDirectory("E:/outputFilesBunched");
	createDirectory(OutputDirectory.substr(0, OutputDirectory.size()-1));

	IOFileName = "E:/InputFiles/Data/";
	string prio = UsePriority == false ? "without" : "with";
	stringstream s;
	s << OutputDirectory << "/" << CacheSize << "-" << prio << ".txt";
	ofstream f(s.str());
	f << "Algo = "<<algo<<endl;//<<", exchange limit = "<<exchange_limit<<endl;
	f << "Cache usage = " << UseCache << endl;
	f << "Cache size = " << CacheSize <<endl;
	f << "Flash size = " << FlashSize <<endl;
	f << "Bring Next Extents = " << BringNextExtents << endl;
	f << "Sequential Time Difference = " << SequentialTimeDiff <<endl;
	f << "Counting hits = " << ConsiderHits <<endl;
	f << "Use Priority = " << UsePriority << endl;
	f << "Devices: "<< SSDCount <<" " <<FCCount<<" "<<SATACount<<endl;
	f << "Desired Response times: "<< desiredRT[0] << " "<< desiredRT[1] << " "<< desiredRT[2] << endl;
	f << "Group Percents: "<<group_percents[0]<<" "<< group_percents[1]<<" "<< group_percents[2]<<endl;
	f << "NumberOfRounds: " << NumberOfRounds << endl; 
	f << "placementDistribution: " << placementDistribution << endl;
	f << "FlashPolicy: " << flashPolicy << endl;
	f << "File: " << AllFiles << endl;
	f << "CacheAlignment: " << CacheAlignment;
	f.close();

	if (flashPolicy == 0)
		cout << "Regular Flash Policy" << endl;
	else if (flashPolicy == 1)
		cout << "Insert To Both Flash Policy" << endl;
	else
		cout << "FAST Flash Policy" << endl;
	// TODO: add this parameter to config

	//ComponentsHolder::getInstance()->getStatisticsHandler()->setDesiredRTPerGroup(desiredRT);

	//createGroupsPriorityFiles();
	//test();
}

void SimulationParameters::readStorageCharacteristics()
{
	std::ifstream input("StorageCharacteristics.cfg");
	char buf[1001];
	int index;
	while(input.good()){
		input.getline(buf, 1000);
		
		string str = string(buf);
		istringstream s(str);
		s >> str;
		if (str == "SSD:")
			s >> SSDSize;
		else if (str == "FC:")
			s >> FCSize;
		else if (str == "SATA:")
			s >> SATASize;
	}
	input.close();
	
	SSDSize = SSDSize*(GB/EXTENT_SIZE);
	FCSize = 0.7*(FCSize*(GB/EXTENT_SIZE));
	SATASize = SATASize*(GB/EXTENT_SIZE);
}

void SimulationParameters::readUserInput(){
	std::ifstream input("E:/InputFiles/Config/Input.cfg");
	char buf[1001];
	int index;
	while(input.good()){
		input.getline(buf, 1000);
		std::string str = std::string(buf);
		if (str.find("DRT1=") != std::string::npos){
			index = str.find("=")+1;
			std::string temp = str.substr(index);
			desiredRT[0]=atof(temp.c_str());
		}
		else if (str.find("DRT2=") != std::string::npos){
			index = str.find("=")+1;
			desiredRT[1]=atof(str.substr(index).c_str());
		}
		else if (str.find("DRT3=") != std::string::npos){
			index = str.find("=")+1;
			desiredRT[2]=atof(str.substr(index).c_str());
		}
		else if (str.find("group_percents1=") != std::string::npos){
			index = str.find("=")+1;
			std::string temp = str.substr(index);
			group_percents[0]=atof(temp.c_str());
		}
		else if (str.find("group_percents2=") != std::string::npos){
			index = str.find("=")+1;
			group_percents[1]=atof(str.substr(index).c_str());
		}
		else if (str.find("group_percents3=") != std::string::npos){
			index = str.find("=")+1;
			group_percents[2]=atof(str.substr(index).c_str());
		}
		else if (str.find("SSDCount=") != std::string::npos){
			index = str.find("=")+1;
			SSDCount=atoi(str.substr(index).c_str());
		}
		else if (str.find("FCCount=") != std::string::npos){
			index = str.find("=")+1;
			FCCount=atof(str.substr(index).c_str());
		}
		else if (str.find("SATACount=") != std::string::npos){
			index = str.find("=")+1;
			SATACount=atof(str.substr(index).c_str());
		}
		else if (str.find("whichAlgo=") != std::string::npos){
			index = str.find("=")+1;
			int algo_num=atoi(str.substr(index).c_str());
			switch(algo_num){
			case 1:
				algo="Optimal";
				exchange_limit=0;
				break;
			case 2:
				algo="Next";
				exchange_limit=0;
				break;
			case 3:
				algo="Cost";
				input.getline(buf, 1000);
				input.getline(buf, 1000);
				std::string str = std::string(buf);
				if (str.find("exchange_limit=") != std::string::npos){
					index = str.find("=")+1;
					exchange_limit=atoi(str.substr(index).c_str());
					cout<<"limit: "<<exchange_limit<<endl;
				}
				break;
			}
		}
		else if (str.find("original_file=") != std::string::npos){
			index = str.find("=")+1;
			string file=str.substr(index);
			istringstream iss(file);
			string fileName;
			AllFiles = "";
			do
			{
				iss >> fileName;
				dataFiles.push_back(fileName);
				AllFiles += fileName + ",";
			}while (iss.good());
			AllFiles = AllFiles.substr(0,AllFiles.size()-1);
		}
	}
}

void SimulationParameters::readParameters()
{
	fstream f("E:/InputFiles/Config/config.txt");
	char buf[1000];
	
	// Simulation time
	f.getline(buf, 100, '=');
	f >> SimulationTime;
	// Cache Size
	f.getline(buf, 100, '=');
	f >> CacheSize;
	
	// cache alignment
	f.getline(buf, 100, '=');
	f >> CacheAlignment;
	
	// flash size
	f.getline(buf, 100, '=');
	f >> FlashSize;
		
	// CachePolicy
	f.getline(buf, 100, '=');
	f >> CachePolicy;
	// CacheReadPart
	f.getline(buf, 100, '=');
	f >> CacheReadPart;
	
	// Write allowance
	f.getline(buf, 100, '=');
	f >> WriteAllowance;

	f.getline(buf, 100, '=');
	f >> UseCache;

	f.getline(buf, 100, '=');
	f >> BringNextExtents;

	f.getline(buf,  100, '=');
	f >> SequentialTimeDiff;

	f.getline(buf,  100, '=');
	f >> ConsiderHits;
	
	f.getline(buf,  100, '=');
	f >> NumberOfRounds;

	f.getline(buf,  100, '=');
	f >> StaticPlacements;
	// UsePriority
	f.getline(buf,  100, '=');
	f >> UsePriority;
	// placementDistribution
	f.getline(buf, 100, '=');
	f >> placementDistribution;


	int policy;
	// insertToBothCaches
	f.getline(buf, 100, '=');
	f >> policy;
	flashPolicy = policy == 0 ? Regular : policy == 1 ? InsertTOBoth : FAST;
	f.close();

	readUserInput();
	readStorageCharacteristics();
	stringstream s, t;
	s.precision(4);
	if (algo=="Cost"){
		s <<"_"<<CachePolicy<< "_"<<algo<<"_"<<exchange_limit<<"_Groups_"<<group_percents[0]<<"-"<<group_percents[1]<<"-"<<group_percents[2]<<"_Devices_"<<SSDCount<<"-"<<FCCount<<"-"<<SATACount<<"_Desired_"<<desiredRT[0]<<"-"<<desiredRT[1]<<"-"<<desiredRT[2]<<".txt";
	}
	else{
		s <<"_"<<CachePolicy<< "_"<<algo<<"_Groups_"<<group_percents[0]<<"-"<<group_percents[1]<<"-"<<group_percents[2]<<"_Devices_"<<SSDCount<<"-"<<FCCount<<"-"<<SATACount<<"_Desired_"<<desiredRT[0]<<"-"<<desiredRT[1]<<"-"<<desiredRT[2]<<".txt";
	}
//	ExtentsFileName += s.str();
	t << AllFiles << "_" << group_percents[0]<<"-"<<group_percents[1]<<"-"<<group_percents[2]<<".txt";
	PriorityFile += t.str();
}

void SimulationParameters::updateDate()
{
	time_t t = time(0);   // get time now
	struct tm * now = localtime( & t );
	std::stringstream s;
	s << now->tm_mday << "." << (now->tm_mon + 1) << " (" << now->tm_hour << "-" << now->tm_min << "-" << now->tm_sec << ")";

	OutputDirectory = "E:/Output/" + CachePolicy + "-" + s.str() + "/";
}

/*
bool SimulationParameters::createGroupsPriorityFiles(int g1, int g2, int g3, int luns_total)
{
	if (g1 + g2 + g3 !=100)
		return false;
	cout << "Creating groups file...\n";
	double group1 = g1 / 100.0;
	double group2 = g2 / 100.0;
	double group3 = g3 / 100.0;
	double g[] = {group1, group2, group3};

	createDirectory("E:/InputFiles/LunsPriorityGroups/");
	stringstream s;
	s.precision(2);
	s << "E:/InputFiles/LunsPriorityGroups/" <<AllFiles << "_" << group1<< "-" << group2 << "-" << group3 << ".txt";
	
	vector<string> groups[3];
	int total = 0;
	map<string,int> luns;
	for (int i = 0; i < dataFiles.size(); i++)
	{
		string fileName = "E:/InputFiles/Data/"+dataFiles[i];
		ifstream input(fileName);
		
		while(input.good())
		{
			char buf[101];
			input.getline(buf,100);
		
			string str = string(buf);
			
			if (str.size() == 0) {
				continue;
			}
			
			istringstream istr (str);
			string type, lunID;
			istr >> type;
			istr >> type;
			if (type!="Read" && type!="Write")
				continue;
			istr >> type;
			istr >> lunID;
			if (lunID == "0" || luns.find(lunID) != luns.end())
				continue;
			luns.insert(pair<string,int>(lunID,0));

			int nof_groups = 3;
			int count=0, random = ((rand()-1) % nof_groups);
			while((groups[random].size() >= luns_total*g[random])){
				if (count==3){
					luns_total++;
					cout << " not enough luns given, adding one more: " << luns_total << endl;
				}
				random = ((random+1) % nof_groups);
				count++;
			}
			groups[random].push_back(lunID);
			
		}
		input.close();
	}
	g1 = group1*luns.size();
	g2 = group2*luns.size();
	g3 = luns.size()-g1-g2;
	cout << "g1 is: " << g1 << endl;
	cout << "g2 is: " << g2 << endl;
	cout << "g3 is: " << g3 << endl;
	cout << "before fix: \n";
	cout << "groups1 size: " << groups[0].size() << endl;
	cout << "groups2 size: " << groups[1].size() << endl;
	cout << "groups3 size: " << groups[2].size() << endl;

	cout << "after fix: \n";
	cout << "groups1 size: " << groups[0].size() << endl;
	cout << "groups2 size: " << groups[1].size() << endl;
	cout << "groups3 size: " << groups[2].size() << endl;
	
	ofstream file (s.str());
	
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < groups[i].size(); j++)
			file << hexaToInt(groups[i][j]) << "\t" << (i+1) << endl;
	file.close();
	cout << "Creating groups file finished\n";
	return true;
	
}
*/

void SimulationParameters::splitFile( int jump ) 
{
	// TODO: add support to multiple files
	ifstream input(IOFileName + dataFiles[0]);
	stringstream s, t;
	int time = jump;
	s << "E:/InputFiles/test";
	int ind = 1;

	while (input.good())
	{
		t =stringstream();
		t << s.str() <<ind++ << ".txt";
		ofstream output(t.str());
		cout << "creating file: " << t.str() << endl;
		string str = "";
		int x = 0;
		int tm = 1000;
		while (input.good())
		{
			if (str.size() > 0 )
				output << str << endl;
			char buf[101];
			//try
			//{
				input.getline(buf,100);
			
				str = string(buf);
			
				if (str.size() == 0)
				{
					continue;
				}
				else
				{
					istringstream istr (str);
					string lineTime;
					istr >> lineTime;
					float curTime = atof(lineTime.c_str());
					if (curTime > tm)
					{
						cout << "time is: " << tm << endl;
						tm += 1000;
					}
					/*
					if (curTime < 43200) 
					{
						str = "";
						continue;
					}
					*/
					if (curTime > 7200)
					{
						output.close();
						input.close();
						return;
					}
					if (curTime > time)
					{
						time += jump;
						break;
					}
				
					output << str << endl;
				}
		
		}
		output.close();
	}
	input.close();


}
/*
void SimulationParameters::createGroupsPriorityFiles()
{
	// check if group file found
	ifstream f(PriorityFile);
	if (!f) 
	{
		// Can't open file
		int g1 = round(group_percents[0],2)*100;
		int g2 = round(group_percents[1],2)*100;
		int g3 = round(group_percents[2],2)*100;
		createGroupsPriorityFiles(g1,g2,g3);
	}
	else
		f.close();
}
*/